#include "Conver.h"
#include <string>

#include "Ctemplate.h"

using namespace std;
using std::char_traits;

//lvgl的图片信息头结构
typedef struct {

    uint32_t cf : 5;          /*Color format: See `lv_img_color_format_t`*/
    uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
                                 non-printable character*/

    uint32_t reserved : 2; /*Reserved to be used later*/

    uint32_t w : 11; /*Width of the image map*/
    uint32_t h : 11; /*Height of the image map*/
} lv_img_header_t;


static uint16_t rgb565_swap(uint16_t rgb565);
static void argb_to_rgb565(const uint8_t *src, uint8_t *dst, int pixel, bool alpha, bool swap);
static uint8_t *rgb888_to_argb888(const uint8_t *src, int pixel);
static void argb888_to_rgb332(const uint8_t *src, uint8_t *dst, int pixel, bool alpha);
static uint8_t *ConverToArray(uint8_t *imgBuf, Image::ImageInfo &info, uint32_t cfg);
static void WriteConverToCFile(FILE *file, uint8_t *converData, Image::ImageInfo &info);
static void ConverToCFile(FILE *file, uint8_t *converData, Image::ImageInfo &info, int imgCf);
static void ConverToBinFile(FILE *file, uint8_t *imgSrc, Image::ImageInfo &info, uint32_t cfg);



void ConverToFile(FILE *file, uint8_t *imgBuf, Image::ImageInfo &info, uint32_t cfg, ConverFile cf)
{
    switch(cf)
    {
        case Conver_C_File:     //转换为c文件
        ConverToCFile(file, imgBuf, info, cfg);
        break;
        case Conver_Bin_File:   //转换为bin文件
        ConverToBinFile(file, imgBuf, info, cfg);
        break;
        default:
        break;
    }
}

/**
 * @brief 将图片数据转换为数组
 * @param file .c文件的文件指针
 * @param imgBuf 图片数据 ARGB8888
 * @param info 原图片信息,转换完成后覆盖上转换图片数据信息
 * @param cfg 转换参数
 * @retval 转换完成后的图片数据
 */
static uint8_t *ConverToArray(uint8_t *imgBuf, Image::ImageInfo &info, uint32_t cfg)
{
    int pixel = info.width * info.height;

    uint8_t *converData = nullptr;
    int dataSize = 0;

    bool alpha = !!(cfg & Conver_Alpha);
    bool swap = (cfg & Conver_16Bit_Swap) == Conver_16Bit_Swap;
    if(cfg & Conver_BigEndian)
    {
        swap = !swap;
    }

    switch (cfg & (~Conver_Alpha))
    {
    case Conver_8Bit:
        dataSize = cfg & Conver_Alpha ? pixel * 2 : pixel;
        info.bpp = cfg & Conver_Alpha ? 16 : 8;
        converData = new uint8_t[dataSize];
        argb888_to_rgb332(imgBuf, converData, pixel, alpha);
        break;
    case Conver_16Bit:
    case Conver_16Bit_Swap:
        dataSize = cfg & Conver_Alpha ? pixel * 3 : pixel * 2;
        info.bpp = cfg & Conver_Alpha ? 24 : 16;
        converData = new uint8_t[dataSize];
        argb_to_rgb565(imgBuf, converData, pixel, alpha, swap);
        break;
    case Conver_32Bit: 
        dataSize = pixel * 4;
        converData = new uint8_t[dataSize];
        if(info.bpp == 24)
        {
            info.bpp = 32;
            converData = rgb888_to_argb888(imgBuf, pixel);      //添加alpha通道
        }
        else
            memcpy(converData, imgBuf, dataSize);       //直接拷贝原图像数据
        if(cfg & Conver_BigEndian)
        {
            for (size_t i = 0; i < dataSize; i+=4)
            {
                uint8_t temp = converData[i+0];
                converData[i+0] = converData[i+2];
                converData[i+2] = temp;
            }
        }
        
        break;
    default:
        break;
    }

    return converData;
}

/**
 * @brief ARGB888转RGB565
 * @param src ARGB8888格式
 * @param dst RGB565格式数据
 * @param pixel 像素点
 * @param alpha 是否保留透明通道(1Byte)
 * @param swap 是否交换RGB565高低字节
 */
static void argb_to_rgb565(const uint8_t *src, uint8_t *dst, int pixel, bool alpha, bool swap)
{
    const uint8_t *pSrc = src;
    uint8_t *pDst = dst;

    for (int i = 0; i < pixel; i++)
    {
        // 获取RGB单色，并截取高位
        unsigned char blue = pSrc[0] >> 3;        //B
        unsigned char green = pSrc[1] >> 2;      //G
        unsigned char red = pSrc[2] >> 3;        //R

        uint16_t rgb565 = red << 11 | green << 5 | blue;

        if(swap)
            rgb565 = rgb565_swap(rgb565);

        pDst[0] = rgb565 & 0xff;    //小端格式
        pDst[1] = rgb565 >> 8;
        pDst += 2;

        if(alpha)               //填入alpha数值1 byte
            *pDst++ = pSrc[3];

        pSrc += 4;
    }
}

/**
 * @brief 交换rgb565高低字节
*/
static uint16_t rgb565_swap(uint16_t rgb565)
{
    return rgb565 >> 8 | rgb565 << 8;
}

/**
 * @brief ARGB888转RGB332
 * @param src ARGB8888格式
 * @param dst RGB332格式数据
 * @param pixel 像素点
 * @param alpha 是否保留透明通道(1Byte)
 */
static void argb888_to_rgb332(const uint8_t *src, uint8_t *dst, int pixel, bool alpha)
{
    const uint8_t *pSrc = src;
    uint8_t *pDst = dst;

    for (int i = 0; i < pixel; i++)
    {
        // 获取RGB单色，并截取高位
        unsigned char blue = pSrc[0] >> 6;        //B
        unsigned char green = pSrc[1] >> 5;      //G
        unsigned char red = pSrc[2] >> 5;        //R

        *pDst++ = red | green | blue;

        if(alpha)               //填入alpha数值1 byte
            *pDst++ = pSrc[3];

        pSrc += 4;
    }
}

/**
 * @brief 在rgb888图片数据中添加alpha通道，alpha数值0xff
 * @param src rgb888图片数据
 * @param pixel 像素点个数 
*/
static uint8_t *rgb888_to_argb888(const uint8_t *src, int pixel)
{
    uint8_t *argb888 = new uint8_t[pixel * 4];

    for (int i = 0; i < pixel; i++)
    {
        argb888[4 * i] = src[3 * i];
        argb888[4 * i + 1] = src[3 * i + 1];
        argb888[4 * i + 2] = src[3 * i + 2];
        argb888[4 * i + 3] = 0xff;
    }

    return argb888;
}

/**
 * @brief 用图片数据生成lvgl 图像C文件
 * @param file C文件的文件指针
 * @param imgSrc 原始图片数据
 * @param info 原始图片数据信息
 * @param imgCf 图片的标志信息 0:true-color 1:true-color-alpha 2:true-color-chormakey
*/
static void ConverToCFile(FILE *file, uint8_t *imgSrc, Image::ImageInfo &info, int imgCf)
{
    Image::ImageInfo imgInfo = info;

    fprintf(file, "%s", templateHeader.c_str());
    fprintf(file, "%s", templateDefine.c_str());

    string mapName = info.name.c_str() + string("_map");
    fprintf(file, templateMapStart.c_str(), mapName.c_str());      


    fprintf(file, "%s", templateMapData8BitStart.c_str());      //开始转换8bit颜色数据
    uint32_t cfg = Conver_8Bit | ((imgCf & Conver_BigEndian)?Conver_BigEndian:0x00) | ((imgCf >> 30) == 1 ? Conver_Alpha : 0);
    uint8_t *convData = ConverToArray(imgSrc, imgInfo, cfg);
    WriteConverToCFile(file, convData, imgInfo);
    delete[] convData;
    fprintf(file, "%s", templateMapDataEnd.c_str());


    fprintf(file, "%s", templateMapData16BitStart.c_str());      //开始转换16bit颜色数据
    cfg = Conver_16Bit | ((imgCf & Conver_BigEndian)?Conver_BigEndian:0x00) | ((imgCf >> 30) == 1 ? Conver_Alpha : 0);
    convData = ConverToArray(imgSrc, imgInfo, cfg);
    WriteConverToCFile(file, convData, imgInfo);
    delete[] convData;
    fprintf(file, "%s", templateMapDataEnd.c_str());


    fprintf(file, "%s", templateMapData16BitSwapStart.c_str());      //开始转换16bit swap颜色数据
    cfg = Conver_16Bit_Swap | ((imgCf & Conver_BigEndian)?Conver_BigEndian:0x00) | ((imgCf >> 30) == 1 ? Conver_Alpha : 0);
    convData = ConverToArray(imgSrc, imgInfo, cfg);
    WriteConverToCFile(file, convData, imgInfo);
    delete[] convData;
    fprintf(file, "%s", templateMapDataEnd.c_str());


    fprintf(file, "%s", templateMapData32BitStart.c_str());      //开始转换32bit颜色数据
    cfg = Conver_32Bit | ((imgCf & Conver_BigEndian)?Conver_BigEndian:0x00) | ((imgCf >> 30) == 1 ? Conver_Alpha : 0);
    convData = ConverToArray(imgSrc, imgInfo, cfg);
    WriteConverToCFile(file, convData, imgInfo);
    delete[] convData;
    fprintf(file, "%s", templateMapDataEnd.c_str());

    
    fprintf(file, "%s", templateMapEnd.c_str());

    fprintf(                //写入lv_img_dst_t描述结构
        file, 
        templateIMGDsc.c_str(), 
        info.name.c_str(),
        imgTrueColorCfgMap[imgCf].c_str(),
        0,
        0,
        imgInfo.width,
        imgInfo.height,
        imgInfo.width * imgInfo.height,
        imgCf == 1 ? alphaByte.c_str() : pixelByte.c_str(),
        mapName.c_str()
    );
}

/**
 * @brief 将转换后的图片数据写入C文件
 * @param file C文件的文件指针
 * @param converData 转换后的图片数据
 * @param info 转换后的图片数据信息
*/
static void WriteConverToCFile(FILE *file, uint8_t *converData, Image::ImageInfo &info)
{
    int total = info.width * info.height * info.bpp / 8;       //总的图片数据
    int index = 0;
    int lineByte = total / info.height;

    while(1)
    {
        for(int i = 0; i < lineByte; i++)
        {
            uint8_t temp = converData[index++];
            if(temp != 0)
                fprintf(file, "%#04x, ", temp);
            else
                fprintf(file, "0x00, ");
        }

        fprintf(file, "\n");
        total -= lineByte;
        if(total <= 0)
            break;
    }
}

/**
 * @brief 用图片数据生成lvgl 图像bin文件
 * @param file bin文件的文件指针
 * @param imgSrc 原始图片数据
 * @param info 原始图片数据信息
 * @param cfg 图片的格式与标志信息
*/
static void ConverToBinFile(FILE *file, uint8_t *imgSrc, Image::ImageInfo &info, uint32_t cfg)
{
    Image::ImageInfo imgInfo = info;

    int imgCf = cfg >> 30;
    uint8_t *convData = ConverToArray(imgSrc, imgInfo, cfg);        //进行数据转换

    int total = imgInfo.width * imgInfo.height * imgInfo.bpp / 8;       //转换后的图片数据

    //写入图片信息头
    lv_img_header_t imgHeader;
    imgHeader.always_zero = 0;
    imgHeader.reserved = 0;
    imgHeader.cf = imgTrueColorCfgMapUint8[imgCf];
    imgHeader.w = imgInfo.width;
    imgHeader.h = imgInfo.height;
    fwrite((void *)&imgHeader, sizeof(imgHeader), 1, file);

    //写入图片数据
    fwrite(convData, total, 1, file);

    delete[] convData;
}
